package pri.zxx.word.util;

import org.apache.poi.xwpf.usermodel.ParagraphAlignment;
import org.apache.poi.xwpf.usermodel.TextAlignment;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;

import java.math.BigInteger;
import java.util.Optional;

//段落构建器
public class XPBuilder {
    //常量，在文档中定义长度和高度的单位
    private static final int PER_LINE = 100;
    //每个字符的单位长度
    private static final int PER_CHART = 100;
    //1厘米≈567
    private static final int PER_CM = 567;
    //每一磅的单位长度
    private static final int PER_POUND = 20;
    //行距单位长度
    private static final int ONE_LINE = 240;

    private final XWPFParagraph paragraph;

    public XPBuilder(XWPFDocument document) {
        this.paragraph = document.createParagraph();
    }

    public static XPBuilder createP(XWPFDocument document) {
        return new XPBuilder(document);
    }


    //设置段落对齐方式
    public XPBuilder align(ParagraphAlignment pAlign, TextAlignment vAlign) {
        if (pAlign != null) {
            paragraph.setAlignment(pAlign);
        }
        if (vAlign != null) {
            paragraph.setVerticalAlignment(vAlign);
        }
        return this;
    }

    //设置段前和段后间距，以磅为单位
    public XPBuilder spaceInPound(Double before, Double after) {
        CTPPr prOfParagraph = getPrOfParagraph(paragraph);
        CTSpacing ctSpacing = Optional.ofNullable(prOfParagraph.getSpacing()).orElse(prOfParagraph.addNewSpacing());
        if (before != null) {
            ctSpacing.setBefore(BigInteger.valueOf((long) (before * PER_POUND)));
        }
        if (after != null) {
            ctSpacing.setAfter(BigInteger.valueOf((long) (after * PER_POUND)));
        }
        return this;
    }


    //设置段前和段后间距，以行为单位
    public XPBuilder spaceInLine(Double beforeLines, Double afterLines) {
        CTPPr prOfParagraph = getPrOfParagraph(paragraph);
        CTSpacing ctSpacing = Optional.ofNullable(prOfParagraph.getSpacing()).orElse(prOfParagraph.addNewSpacing());
        if (beforeLines != null) {
            ctSpacing.setBeforeLines(BigInteger.valueOf((long) (beforeLines * PER_LINE)));
        }
        if (afterLines != null) {
            ctSpacing.setAfterLines(BigInteger.valueOf((long) (afterLines * PER_LINE)));
        }
        return this;
    }

    //设置段落行距
    public XPBuilder lineSpace(double value, STLineSpacingRule.Enum spaceRule) {
        CTPPr prOfParagraph = getPrOfParagraph(paragraph);
        CTSpacing ctSpacing = Optional.ofNullable(prOfParagraph.getSpacing()).orElse(prOfParagraph.addNewSpacing());
        int unit;
        if (spaceRule == null) {
            spaceRule = STLineSpacingRule.AUTO;
        }
        if (spaceRule == STLineSpacingRule.AUTO) {
            //当行距规则为多倍行距时，单位为行，且最小为0.06行
            unit = ONE_LINE;
            if (value < 0.06) {
                value = 0.06;
            }
        } else {
            //当行距规则为固定值或最小值时，单位为磅，且最小为0.7磅
            unit = PER_POUND;
            if (value < 0.7) {
                value = 0.7;
            }
        }
        ctSpacing.setLine(BigInteger.valueOf((long) (value * unit)));
        ctSpacing.setLineRule(spaceRule);
        return this;
    }

    //设置段落缩进，以厘米为单位; 悬挂缩进高于首行缩进；右侧缩进高于左侧缩进
    public XPBuilder indentInCM(double firstLine, double hanging, double right, double left) {
        CTPPr prOfParagraph = getPrOfParagraph(paragraph);
        CTInd ctInd = Optional.ofNullable(prOfParagraph.getInd()).orElse(prOfParagraph.addNewInd());
        if (firstLine != 0) {
            ctInd.setFirstLine(BigInteger.valueOf((long) (firstLine * PER_CM)));
        }
        if (hanging != 0) {
            ctInd.setHanging(BigInteger.valueOf((long) (hanging * PER_CM)));
        }
        if (right != 0) {
            ctInd.setRight(BigInteger.valueOf((long) (right * PER_CM)));
        }
        if (left != 0) {
            ctInd.setLeft(BigInteger.valueOf((long) (left * PER_CM)));
        }
        return this;
    }

    //设置段落缩进，以字符为单位; 悬挂缩进高于首行缩进；右侧缩进高于左侧缩进
    public XPBuilder indentInChart(int firstLine, int hanging, int left, int right) {
        CTPPr prOfParagraph = getPrOfParagraph(paragraph);
        CTInd ctInd = Optional.ofNullable(prOfParagraph.getInd()).orElse(prOfParagraph.addNewInd());
        if (firstLine != 0) {
            ctInd.setFirstLineChars(BigInteger.valueOf((long) firstLine * PER_CHART));
        }
        if (hanging != 0) {
            ctInd.setHangingChars(BigInteger.valueOf((long) hanging * PER_CHART));
        }
        if (right != 0) {
            ctInd.setRightChars(BigInteger.valueOf((long) right * PER_CHART));
        }
        if (left != 0) {
            ctInd.setLeftChars(BigInteger.valueOf((long) left * PER_CHART));
        }
        return this;
    }

    /**
     * 段落其他属性
     */
    public XPBuilder overflowPunct(Boolean overflowPunct) {
        if (overflowPunct != null) {
            CTOnOff ctOnOff = getPrOfParagraph(paragraph).addNewOverflowPunct();
            ctOnOff.setVal(overflowPunct ? STOnOff.ON : STOnOff.OFF);
        }
        return this;
    }

    public XPBuilder copyStyle(XWPFParagraph otherPra) {
        paragraph.getCTP().setPPr(getPrOfParagraph(otherPra));
        return this;
    }

    public XWPFParagraph build() {
        return paragraph;
    }

    public static CTPPr getPrOfParagraph(XWPFParagraph p) {
        CTP ctp = p.getCTP();
        if (ctp == null) {
            throw new RuntimeException("段落缺失CTP属性");
        }
        return Optional.of(ctp).map(c -> Optional.ofNullable(c.getPPr()).orElse(c.addNewPPr())).orElse(null);
    }
}